package com.googlecode.tinyjava.parser.symboltable;
import com.googlecode.tinyjava.symboltable.*;
import com.googlecode.tinyjava.symboltable.Class;
import com.googlecode.tinyjava.symboltable.Package;

public class Parser {
	public static final int _EOF = 0;
	public static final int _ident = 1;
	public static final int _number = 2;
	public static final int _stringconst = 3;
	public static final int _charconst = 4;
	public static final int maxT = 54;

	static final boolean T = true;
	static final boolean x = false;
	static final int minErrDist = 2;

	public Token t;    // last recognized token
	public Token la;   // lookahead token
	int errDist = minErrDist;
	
	public Scanner scanner;
	public Errors errors;

	public SymbolTable symbolTable;

boolean isDataItemDeclaration() {
	Token x = la;
	if ("final".equals(x.val))
		x = scanner.Peek();
	x = scanner.Peek();
	x = scanner.Peek();
	return ";".equals(x.val) || "=".equals(x.val);
}

boolean isAssignment() {
	Token x = la;
	return "=".equals(x.val);
}

boolean isTypeCast() {
	if (la.kind == 18 || la.kind == 19 || la.kind == 20 || la.kind == 21 || la.kind == 22)
		return true;
	return false;
}



	public Parser(Scanner scanner) {
		this.scanner = scanner;
		errors = new Errors();
	}

	void SynErr (int n) {
		if (errDist >= minErrDist) errors.SynErr(la.line, la.col, n);
		errDist = 0;
	}

	public void SemErr (String msg) {
		if (errDist >= minErrDist) errors.SemErr(t.line, t.col, msg);
		errDist = 0;
	}
	
	void Get () {
		for (;;) {
			t = la;
			la = scanner.Scan();
			if (la.kind <= maxT) {
				++errDist;
				break;
			}

			la = t;
		}
	}
	
	void Expect (int n) {
		if (la.kind==n) Get(); else { SynErr(n); }
	}
	
	boolean StartOf (int s) {
		return set[s][la.kind];
	}
	
	void ExpectWeak (int n, int follow) {
		if (la.kind == n) Get();
		else {
			SynErr(n);
			while (!StartOf(follow)) Get();
		}
	}
	
	boolean WeakSeparator (int n, int syFol, int repFol) {
		int kind = la.kind;
		if (kind == n) { Get(); return true; }
		else if (StartOf(repFol)) return false;
		else {
			SynErr(n);
			while (!(set[syFol][kind] || set[repFol][kind] || set[0][kind])) {
				Get();
				kind = la.kind;
			}
			return StartOf(syFol);
		}
	}
	
	void TinyJava() {
		Expect(5);
		Expect(1);
		symbolTable = new SymbolTable(new Package(t.val)); 
		Expect(6);
		while (la.kind == 7) {
			ClassDeclaration();
		}
		symbolTable.print(); 
	}

	void ClassDeclaration() {
		Expect(7);
		Expect(1);
		symbolTable.add(new Class(t.val)); 
		ClassBody();
		symbolTable.back(); 
	}

	void ClassBody() {
		Expect(8);
		while (StartOf(1)) {
			ClassItemDeclaration();
		}
		Expect(9);
	}

	void ClassItemDeclaration() {
		if (isDataItemDeclaration()) {
			DataItemDeclaration();
		} else if (StartOf(2)) {
			MethodDeclaration();
		} else SynErr(55);
	}

	void DataItemDeclaration() {
		boolean isFinal = false; 
		if (la.kind == 10) {
			Get();
			isFinal = true; 
		}
		Type();
		String type = t.val; 
		Expect(1);
		symbolTable.add(new Variable(t.val, type, isFinal)); 
		if (la.kind == 11) {
			Get();
			Expression();
		}
		Expect(6);
	}

	void MethodDeclaration() {
		if (StartOf(3)) {
			MethodHeader();
		}
		MethodBlock();
		symbolTable.back(); 
	}

	void Type() {
		if (StartOf(4)) {
			StdType();
			if (la.kind == 15) {
				Get();
				Expect(16);
			}
		} else if (la.kind == 17) {
			Get();
		} else SynErr(56);
	}

	void Expression() {
		OrElseExpression();
	}

	void MethodHeader() {
		ReturnType();
		String returnType = t.val; 
		Expect(1);
		symbolTable.add(new Method(t.val, returnType)); 
		FormalParams();
	}

	void MethodBlock() {
		Expect(8);
		while (StartOf(5)) {
			if (StartOf(6)) {
				DataItemDeclaration();
			} else {
				Statement();
			}
		}
		Expect(9);
	}

	void ReturnType() {
		if (StartOf(4)) {
			StdType();
		} else if (la.kind == 23) {
			Get();
		} else SynErr(57);
	}

	void FormalParams() {
		Expect(12);
		if (StartOf(7)) {
			FormalParam();
			while (la.kind == 13) {
				Get();
				FormalParam();
			}
		}
		Expect(14);
	}

	void FormalParam() {
		Type();
		String type = t.val; 
		Expect(1);
		symbolTable.add(new Variable(t.val, type, false)); 
	}

	void Statement() {
		if (la.kind == 8) {
			Block();
		} else if (la.kind == 6) {
			Get();
		} else if (isAssignment()) {
			Assignment();
			Expect(6);
		} else if (la.kind == 1) {
			MethodCall();
			Expect(6);
		} else if (la.kind == 24) {
			Get();
			Expect(12);
			Expression();
			Expect(14);
			Block();
			if (la.kind == 25) {
				Get();
				Block();
			}
		} else if (la.kind == 26) {
			Get();
			Expect(12);
			Expression();
			Expect(14);
			Statement();
		} else if (la.kind == 27) {
			Get();
			Statement();
			Expect(26);
			Expect(12);
			Expression();
			Expect(14);
			Expect(6);
		} else if (la.kind == 28) {
			Get();
			if (la.kind == 1) {
				Get();
			}
			Expect(6);
		} else if (la.kind == 29) {
			Get();
			if (la.kind == 1) {
				Get();
			}
			Expect(6);
		} else if (la.kind == 30) {
			Get();
			if (StartOf(8)) {
				Expression();
			}
			Expect(6);
		} else SynErr(58);
	}

	void StdType() {
		if (StartOf(9)) {
			NumType();
		} else if (la.kind == 18) {
			Get();
		} else SynErr(59);
	}

	void NumType() {
		if (la.kind == 20 || la.kind == 21 || la.kind == 22) {
			IntType();
		} else if (la.kind == 19) {
			Get();
		} else SynErr(60);
	}

	void IntType() {
		if (la.kind == 20) {
			Get();
		} else if (la.kind == 21) {
			Get();
		} else if (la.kind == 22) {
			Get();
		} else SynErr(61);
	}

	void Block() {
		Expect(8);
		while (StartOf(10)) {
			Statement();
		}
		Expect(9);
	}

	void Assignment() {
		Designator();
		Expect(11);
		Expression();
	}

	void MethodCall() {
		Expect(1);
		Expect(12);
		if (StartOf(8)) {
			ActualParams();
		}
		Expect(14);
	}

	void ActualParams() {
		Expression();
		while (la.kind == 13) {
			Get();
			Expression();
		}
	}

	void Designator() {
		Expect(1);
		if (la.kind == 12) {
			Get();
			if (StartOf(8)) {
				ActualParams();
			}
			Expect(14);
		}
	}

	void OrElseExpression() {
		AndThenExpression();
		while (la.kind == 31) {
			Get();
			AndThenExpression();
		}
	}

	void AndThenExpression() {
		OrExpression();
		while (la.kind == 32) {
			Get();
			OrExpression();
		}
	}

	void OrExpression() {
		XorExpression();
		while (la.kind == 33) {
			Get();
			XorExpression();
		}
	}

	void XorExpression() {
		AndExpression();
		while (la.kind == 34) {
			Get();
			AndExpression();
		}
	}

	void AndExpression() {
		EqualExpression();
		while (la.kind == 35) {
			Get();
			EqualExpression();
		}
	}

	void EqualExpression() {
		RelExpression();
		while (la.kind == 36 || la.kind == 37) {
			if (la.kind == 36) {
				Get();
			} else {
				Get();
			}
			RelExpression();
		}
	}

	void RelExpression() {
		ShiftExpression();
		while (StartOf(11)) {
			if (la.kind == 38) {
				Get();
			} else if (la.kind == 39) {
				Get();
			} else if (la.kind == 40) {
				Get();
			} else {
				Get();
			}
			ShiftExpression();
		}
	}

	void ShiftExpression() {
		AddExpression();
		while (la.kind == 42 || la.kind == 43 || la.kind == 44) {
			if (la.kind == 42) {
				Get();
			} else if (la.kind == 43) {
				Get();
			} else {
				Get();
			}
			AddExpression();
		}
	}

	void AddExpression() {
		MulExpression();
		while (la.kind == 45 || la.kind == 46) {
			if (la.kind == 45) {
				Get();
			} else {
				Get();
			}
			MulExpression();
		}
	}

	void MulExpression() {
		Unary();
		while (la.kind == 47 || la.kind == 48 || la.kind == 49) {
			if (la.kind == 47) {
				Get();
			} else if (la.kind == 48) {
				Get();
			} else {
				Get();
			}
			Unary();
		}
	}

	void Unary() {
		while (la.kind == 46 || la.kind == 50 || la.kind == 51) {
			if (la.kind == 46) {
				Get();
			} else if (la.kind == 50) {
				Get();
			} else {
				Get();
			}
		}
		Primary();
	}

	void Primary() {
		if (StartOf(12)) {
			Literal();
		} else if (la.kind == 1) {
			Designator();
		} else if (la.kind == 12) {
			Get();
			Expression();
			Expect(14);
		} else SynErr(62);
	}

	void Literal() {
		if (la.kind == 2) {
			Get();
		} else if (la.kind == 4) {
			Get();
		} else if (la.kind == 3) {
			Get();
		} else if (la.kind == 52) {
			Get();
		} else if (la.kind == 53) {
			Get();
		} else SynErr(63);
	}



	public void Parse() {
		la = new Token();
		la.val = "";		
		Get();
		TinyJava();
		Expect(0);

	}

	private static final boolean[][] set = {
		{T,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x},
		{x,x,x,x, x,x,x,x, T,x,T,x, x,x,x,x, x,T,T,T, T,T,T,T, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x},
		{x,x,x,x, x,x,x,x, T,x,x,x, x,x,x,x, x,x,T,T, T,T,T,T, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x},
		{x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,T,T, T,T,T,T, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x},
		{x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,T,T, T,T,T,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x},
		{x,T,x,x, x,x,T,x, T,x,T,x, x,x,x,x, x,T,T,T, T,T,T,x, T,x,T,T, T,T,T,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x},
		{x,x,x,x, x,x,x,x, x,x,T,x, x,x,x,x, x,T,T,T, T,T,T,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x},
		{x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,T,T,T, T,T,T,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x},
		{x,T,T,T, T,x,x,x, x,x,x,x, T,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,T,x, x,x,T,T, T,T,x,x},
		{x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,T, T,T,T,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x},
		{x,T,x,x, x,x,T,x, T,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, T,x,T,T, T,T,T,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x},
		{x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,T,T, T,T,x,x, x,x,x,x, x,x,x,x, x,x,x,x},
		{x,x,T,T, T,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, T,T,x,x}

	};
} // end Parser


class Errors {
	public int count = 0;                                    // number of errors detected
	public java.io.PrintStream errorStream = System.out;     // error messages go to this stream
	public String errMsgFormat = "-- line {0} col {1}: {2}"; // 0=line, 1=column, 2=text
	
	protected void printMsg(int line, int column, String msg) {
		StringBuffer b = new StringBuffer(errMsgFormat);
		int pos = b.indexOf("{0}");
		if (pos >= 0) { b.delete(pos, pos+3); b.insert(pos, line); }
		pos = b.indexOf("{1}");
		if (pos >= 0) { b.delete(pos, pos+3); b.insert(pos, column); }
		pos = b.indexOf("{2}");
		if (pos >= 0) b.replace(pos, pos+3, msg);
		errorStream.println(b.toString());
	}
	
	public void SynErr (int line, int col, int n) {
		String s;
		switch (n) {
			case 0: s = "EOF expected"; break;
			case 1: s = "ident expected"; break;
			case 2: s = "number expected"; break;
			case 3: s = "stringconst expected"; break;
			case 4: s = "charconst expected"; break;
			case 5: s = "\"package\" expected"; break;
			case 6: s = "\";\" expected"; break;
			case 7: s = "\"class\" expected"; break;
			case 8: s = "\"{\" expected"; break;
			case 9: s = "\"}\" expected"; break;
			case 10: s = "\"final\" expected"; break;
			case 11: s = "\"=\" expected"; break;
			case 12: s = "\"(\" expected"; break;
			case 13: s = "\",\" expected"; break;
			case 14: s = "\")\" expected"; break;
			case 15: s = "\"[\" expected"; break;
			case 16: s = "\"]\" expected"; break;
			case 17: s = "\"ISR\" expected"; break;
			case 18: s = "\"boolean\" expected"; break;
			case 19: s = "\"char\" expected"; break;
			case 20: s = "\"byte\" expected"; break;
			case 21: s = "\"short\" expected"; break;
			case 22: s = "\"int\" expected"; break;
			case 23: s = "\"void\" expected"; break;
			case 24: s = "\"if\" expected"; break;
			case 25: s = "\"else\" expected"; break;
			case 26: s = "\"while\" expected"; break;
			case 27: s = "\"do\" expected"; break;
			case 28: s = "\"break\" expected"; break;
			case 29: s = "\"continue\" expected"; break;
			case 30: s = "\"return\" expected"; break;
			case 31: s = "\"||\" expected"; break;
			case 32: s = "\"&&\" expected"; break;
			case 33: s = "\"|\" expected"; break;
			case 34: s = "\"^\" expected"; break;
			case 35: s = "\"&\" expected"; break;
			case 36: s = "\"==\" expected"; break;
			case 37: s = "\"!=\" expected"; break;
			case 38: s = "\"<\" expected"; break;
			case 39: s = "\">\" expected"; break;
			case 40: s = "\"<=\" expected"; break;
			case 41: s = "\">=\" expected"; break;
			case 42: s = "\"<<\" expected"; break;
			case 43: s = "\">>\" expected"; break;
			case 44: s = "\">>>\" expected"; break;
			case 45: s = "\"+\" expected"; break;
			case 46: s = "\"-\" expected"; break;
			case 47: s = "\"*\" expected"; break;
			case 48: s = "\"/\" expected"; break;
			case 49: s = "\"%\" expected"; break;
			case 50: s = "\"~\" expected"; break;
			case 51: s = "\"!\" expected"; break;
			case 52: s = "\"true\" expected"; break;
			case 53: s = "\"false\" expected"; break;
			case 54: s = "??? expected"; break;
			case 55: s = "invalid ClassItemDeclaration"; break;
			case 56: s = "invalid Type"; break;
			case 57: s = "invalid ReturnType"; break;
			case 58: s = "invalid Statement"; break;
			case 59: s = "invalid StdType"; break;
			case 60: s = "invalid NumType"; break;
			case 61: s = "invalid IntType"; break;
			case 62: s = "invalid Primary"; break;
			case 63: s = "invalid Literal"; break;
			default: s = "error " + n; break;
		}
		printMsg(line, col, s);
		count++;
	}

	public void SemErr (int line, int col, String s) {	
		printMsg(line, col, s);
		count++;
	}
	
	public void SemErr (String s) {
		errorStream.println(s);
		count++;
	}
	
	public void Warning (int line, int col, String s) {	
		printMsg(line, col, s);
	}
	
	public void Warning (String s) {
		errorStream.println(s);
	}
} // Errors


class FatalError extends RuntimeException {
	public static final long serialVersionUID = 1L;
	public FatalError(String s) { super(s); }
}
